package parser;

import java.io.File;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

import javax.script.ScriptContext;
import javax.script.ScriptEngine;

import org.apache.commons.lang3.StringEscapeUtils;
import org.eclipse.jetty.io.WriterOutputStream;

import bsh.BshScriptEngineFactory;

public class Parser {

	public static String BSH_CODE_START_TAG = "<?";
	public static String BSH_CODE_START_TAG_2 = "<?phpj";
	public static String BSH_PRINT_START_TAG = "<?=";
	public static String BSH_CODE_END_TAG = "?>";
	
	private File file;
	private Integer line = -1;

	public Parser(File file) {
		this.file = file;
	}
	
	
	
	public void parse(Map<String, Object> data, Writer writer) throws Exception {
		Scanner scanner = new Scanner(file);
		String script = readFile(file.getAbsolutePath(), Charset.forName("utf-8"));
		
		HashMap<Integer,String> scriptLines = new HashMap();
		

		int i = 0;
		Integer pos;
		while((pos = script.indexOf(BSH_CODE_START_TAG, i++)) != -1)
			scriptLines.put(pos, "START");
		i = 0;
		while((pos = script.indexOf(BSH_CODE_END_TAG, i++)) != -1)
			scriptLines.put(pos, "STOP");
		
		Object[] sortedArr = scriptLines.keySet().toArray();
		

		Integer[] sorted = new Integer[sortedArr.length];
		for(int line = 0; line < sorted.length; line++)
			sorted[line] = (Integer) sortedArr[line];
		
		Arrays.sort(sorted);
		
		StringBuilder sb = new StringBuilder();		
		
		Integer nextTagNr;
		if(sorted.length == 0) {
			System.out.println("No php code in script");
			nextTagNr = script.length();
		} else {
			nextTagNr = sorted[0];
		}


		Integer tagNr = 0;
		Integer atPosition = 0;
		line = 1;
		while(true) {
			String staticText = script.substring(atPosition, nextTagNr);
			if(staticText != "") {
				for(String scriptLine : staticText.split("\n")) {
					//sb.append("parser.setLine(" + line + ");");
					sb.append("out.print(\"");
					sb.append(StringEscapeUtils.escapeJava(scriptLine));
					sb.append("\\n\");\n");
					line++;
				}
			}
			//at start tag
			atPosition = nextTagNr;
			if(atPosition == script.length()) {
				break;
			}
			boolean print_mode = false;
			if(script.substring(atPosition, atPosition+BSH_CODE_START_TAG.length()).startsWith(BSH_CODE_START_TAG)) {
				if(script.substring(atPosition, atPosition+BSH_CODE_START_TAG_2.length()).startsWith(BSH_CODE_START_TAG_2))
					atPosition += BSH_CODE_START_TAG_2.length();
				else if(script.substring(atPosition, atPosition+BSH_PRINT_START_TAG.length()).startsWith(BSH_PRINT_START_TAG)) {
					atPosition += BSH_PRINT_START_TAG.length();
					print_mode = true;
				}
				else
					atPosition += BSH_CODE_START_TAG.length();
			} else if(scriptLines.get(nextTagNr).equals("STOP"))
				throw new Exception("Error parsing " + file.getName() + ": End tag '" + scriptLines.get(nextTagNr) + "' when expecting text or '<?'");

			//add code
			nextTagNr = sorted[++tagNr];
			sb.append("//Code\n\n");
			staticText = script.substring(atPosition, nextTagNr).trim();
			if(staticText != "") {
				for(String scriptLine : staticText.split("\n")) {
					scriptLine = scriptLine.trim();
					//sb.append("parser.setLine(" + line + ");");
					if(print_mode)
						sb.append("out.print(");
					//typo fix
					if(print_mode && scriptLine.endsWith(");"))
						scriptLine = scriptLine.substring(0, scriptLine.length()-1);
					sb.append(scriptLine);
					if(print_mode)
						sb.append(");");
					//sb.append("\n");
					if(scriptLine.contains("\n"))
						line++;
				}
			}
			sb.append("//Text\n\n");
			atPosition = nextTagNr;
			
			if(scriptLines.get(nextTagNr).equals("START"))
				throw new Exception("Error parsing " + file.getName() + ": Start tag '" + scriptLines.get(nextTagNr) + "' when expecting text or '<?'");

			if(tagNr >= sorted.length-1)
				nextTagNr = script.length();
			else
				nextTagNr = sorted[++tagNr];
			atPosition += BSH_CODE_END_TAG.length();
			//if at end, parse remaining
		}
		
		//System.out.println("parsed script " + file + " - printing generated code");
		//System.out.println(sb.toString());

		ScriptEngine scriptEngine = new BshScriptEngineFactory().getScriptEngine();
		scriptEngine.getContext().setWriter(writer);
		scriptEngine.getContext().setErrorWriter(writer);
		for(Entry<String, Object> entry : data.entrySet()) {
			scriptEngine.getContext().setAttribute(entry.getKey(), entry.getValue(), ScriptContext.ENGINE_SCOPE);
			System.out.println("context setAttribute " + entry.getKey() + "=" + entry.getValue());
		}
		scriptEngine.getContext().setAttribute("out", new PrintStream(new WriterOutputStream(writer)), ScriptContext.ENGINE_SCOPE);
		scriptEngine.getContext().setAttribute("parser", this, ScriptContext.ENGINE_SCOPE);
		
		String generated = sb.toString();
	
		scriptEngine.eval(generated);


	}
	private static String readFile(String path, Charset encoding) throws IOException {
		byte[] encoded = Files.readAllBytes(Paths.get(path));
		return encoding.decode(ByteBuffer.wrap(encoded)).toString();
	}



	public Integer getLine() {
		// TODO Auto-generated method stub
		return line ;
	}
	public void setLine(int line) {
		this.line = line;
	}
}
